﻿using Masuit.Tools.Security;
using Masuit.Tools.Systems;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Caching.Memory;
using PMP.Application.Models;
using PMP.Core.Entities;
using PMP.Core.Enums;
using PMP.Core.Extensions;
using PMP.Core.SignalR;
using PMP.Core.SqlSugar;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;

namespace PMP.Application.Apis;

/// <summary>
/// 项目管理
/// </summary>
public class ProjectApi: IDynamicApiController
{
    private readonly Repository<TbProject> _projRepo;
    private readonly Repository<TbProjectAttachment> _projAttRepo;
    private readonly IMemoryCache _memoryCache;
    private readonly MsgHub _msgHub;
    
    public ProjectApi(IMemoryCache memoryCache
        , Repository<TbProject> projRepo, Repository<TbProjectAttachment> projAttRepo
        , MsgHub msgHub)
    {
        _memoryCache = memoryCache;
        _projRepo = projRepo;
        _projAttRepo = projAttRepo;
        _msgHub = msgHub;
    }

    /// <summary>
    /// 创建项目
    /// </summary>
    /// <param name="param"></param>
    /// <returns></returns>
    [AllowAnonymous]
    public async Task<int> Add(AddProjectParam param)
    {
        var project = param.Adapt<TbProject>();
        project.ProjectLevel = EnumProjectLevel.Level0;

        if (!string.IsNullOrWhiteSpace(param.InvitationCode))
        {
            var isStr = param.InvitationCode.AESDecrypt("");
            if (int.TryParse(isStr, out var userId))
            {
                var account = await DbContext.Instance.Queryable<TbAccount>()
                    .Where(a => a.Id == userId)
                    .FirstAsync();
                if (account == null)
                    throw Oops.Bah("邀请码有误");
                project.ResponsibleUserId = userId;
                project.ResponsibleUserName = account.RealName;
            }
            else
            {
                throw Oops.Bah("邀请码有误");
            }
        }

        try
        {
            await DbContext.Instance.BeginTranAsync();

            var num = await DbContext.Instance.Queryable<TbProject>()
                .Where(a => SqlFunc.DateIsSame(a.CreateTime, DateTime.Today))
                .MaxAsync<int>(a => a.ProjectNum);
            project.ProjectNum = (num + 1);
            project.ProjectCode = $"{DateTime.Today:yyyyMMdd}{(num + 1).ToString().PadLeft(3, '0')}";
            project = await DbContext.Instance.Insertable(project).ExecuteReturnEntityAsync();
            
            // 初始进度
            var progressModel = new TbProjectProgress();
            progressModel.Content = param.FirstProgress;
            progressModel.ProjectId = project.Id;
            await DbContext.Instance.Insertable(progressModel).ExecuteCommandAsync();
            
            await DbContext.Instance.CommitTranAsync();
        }
        catch (Exception e)
        {
            await DbContext.Instance.RollbackTranAsync();
            throw;
        }
        return project.Id;
    }
    
    
    /// <summary>
    /// 上传项目附件
    /// </summary>
    /// <param name="projectId">项目id</param>
    /// <param name="attachmentType">附件类型（0：项目资料；1：会议资料）</param>
    /// <param name="files">附件</param>
    public async Task<List<TbProjectAttachment>> AddAttachment([FromForm]int projectId,[FromForm] int attachmentType, [FromForm]List<IFormFile> files )
    {
        if (files?.Any() != true)
            throw Oops.Bah("请上传附件");
        
        var batchCode = Guid.NewGuid().ToString();
        var attachments = new List<TbProjectAttachment>();
        var folder  = "./Files/ProjectAttachment/" ;
        Directory.CreateDirectory(folder);
        foreach (var file in files)
        {
            var filePath = $"{folder}{Guid.NewGuid().ToString("N").Substring(0,4)}_{file.FileName}";
            await using (var stream = new FileStream(filePath, FileMode.Create))
            {
                await file.CopyToAsync(stream);
            }
            var attachment = new TbProjectAttachment
            {
                ProjectId = projectId,
                AttachmentType = attachmentType,
                FileName = file.FileName,
                FilePath = filePath,
                BatchCode = batchCode
            };
            attachments.Add(attachment);
        }
        await DbContext.Instance.Insertable(attachments).ExecuteCommandAsync();
        attachments = await DbContext.Instance.Queryable<TbProjectAttachment>()
            .Where(a => a.BatchCode == batchCode)
            .ToListAsync();
        return attachments;
    }

    /// <summary>
    /// 增加项目进度
    /// </summary>
    /// <param name="projectId">项目id</param>
    /// <param name="title">进度标题</param>
    /// <param name="content">进度内容</param>
    /// <param name="projectFiles">项目资料</param>
    /// <param name="meetingFiles">会议资料</param>
    public async Task AddProgress([FromForm] int projectId,[FromForm]string title, [FromForm] string content
        , [FromForm]List<IFormFile> projectFiles,[FromForm]List<IFormFile> meetingFiles)
    {
        var batchCode = Guid.NewGuid().ToString();
        var progress = new TbProjectProgress();
        progress.ProjectId = projectId;
        progress.Content = content;
        progress.Title = string.IsNullOrWhiteSpace(title)?"项目进展" : title;
        
        try
        {
            await DbContext.Instance.BeginTranAsync();
            progress = await DbContext.Instance.Insertable(progress).ExecuteReturnEntityAsync();
            // 附件
            var attachments = new List<TbProjectAttachment>();
            if (projectFiles?.Any() == true)
            {
                var folder  = "./Files/ProjectAttachment/" ;
                Directory.CreateDirectory(folder);
                foreach (var file in projectFiles)
                {
                    var filePath =$"{folder}{Guid.NewGuid().ToString("N").Substring(0,4)}_{file.FileName}";
                    await using (var stream = new FileStream(filePath, FileMode.Create))
                    {
                        await file.CopyToAsync(stream);
                    }
                    var attachment = new TbProjectAttachment
                    {
                        ProjectId = projectId,
                        ProgressId = progress.Id,
                        AttachmentType = 0,
                        FileName = file.FileName,
                        FilePath = filePath,
                        BatchCode = batchCode
                    };
                    attachments.Add(attachment);
                }
            }
            if (meetingFiles?.Any() == true)
            {
                var folder  = "./Files/ProjectAttachment/" ;
                Directory.CreateDirectory(folder);
                foreach (var file in meetingFiles)
                {
                    var filePath = $"{folder}{Guid.NewGuid().ToString("N").Substring(0,4)}_{file.FileName}";
                    await using (var stream = new FileStream(filePath, FileMode.Create))
                    {
                        await file.CopyToAsync(stream);
                    }
                    var attachment = new TbProjectAttachment
                    {
                        ProjectId = projectId,
                        AttachmentType = 1,
                        FileName = file.FileName,
                        ProgressId = progress.Id,
                        FilePath = filePath,
                        BatchCode = batchCode
                    };
                    attachments.Add(attachment);
                }
            }
            await DbContext.Instance.Insertable(attachments).ExecuteCommandAsync();
            await DbContext.Instance.CommitTranAsync();
            
            
            // @提及处理
            var userIds =  CommonHelper.GetUserIdFromContent(content);
            foreach (var userId in userIds)
            {
                await _msgHub.SendProjectNotify(userId, projectId, $"项目进度有更新，需要您处理。");
            }
            
        }
        catch (Exception e)
        {
            await DbContext.Instance.RollbackTranAsync();
            throw;
        }
    }

    
    /// <summary>
    /// 更新项目
    /// </summary>
    /// <param name="param"></param>
    /// <returns></returns>
    public async Task Update(TbProject param)
    {
        param.UpdateUserName = App.User.GetRealName();
        param.UpdateUserId = App.User.GetUserId();
        param.UpdateTime = DateTime.Now;
        await DbContext.Instance.Updateable(param)
            .IgnoreNullColumns()
            .IgnoreColumns(a => new
            {
                a.CreateUserId,
                a.CreateUserName,
                a.CreateTime 
            })
            .ExecuteCommandAsync();
    }

    /// <summary>
    /// 完结项目
    /// </summary>
    /// <param name="projectId">项目 id</param>
    public async Task Finish([FromForm] int projectId)
    {
        await DbContext.Instance.Updateable<TbProject>()
            .SetColumns(a => new ()
            {
                IsFinished = true,
                UpdateTime = DateTime.Now,
                UpdateUserId = App.User.GetUserId(),
                UpdateUserName = App.User.GetRealName()
            })
            .Where(a => a.Id == projectId)
            .ExecuteCommandAsync();
        await DbContext.Instance.Insertable<TbProjectProgress>(new TbProjectProgress()
        {
            ProjectId = projectId,
            Content = "完结"
        }).ExecuteCommandAsync();
    }
    
    /// <summary>
    /// 获取项目信息
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public async Task<TbProject> Get(int id)
    {
        var res = await _projRepo.GetByIdAsync(id);
        return res;
    }
    /// <summary>
    /// 获取项目附件列表
    /// </summary>
    /// <param name="projectId">项目id</param>
    /// <returns></returns>
    public async Task<List<TbProjectAttachment>> GetAttachments(int projectId)
    {
        var res = await _projAttRepo.GetListAsync(a => a.ProjectId == projectId);
        return res;
    }

    /// <summary>
    /// 获取项目进度列表
    /// </summary>
    /// <param name="projectId"></param>
    /// <returns></returns>
    public async Task<List<TbProjectProgress>> GetProgress(int projectId)
    {
        var res = await DbContext.Instance.Queryable<TbProjectProgress>()
            .Where(a => a.ProjectId == projectId)
            .ToListAsync();
        return res;
    }
    /// <summary>
    /// 获取项目列表
    /// </summary>
    /// <param name="projectLevel">项目等级</param>
    /// <param name="accountId">发起人或负责人Id</param>
    /// <returns></returns>
    public async Task<List<ProjectDisplayResponse>> GetList([FromForm] int? projectLevel,[FromForm]int? accountId)
    {
        var res = await DbContext.Instance.Queryable<TbProject>()
            .WhereIF(Enum.TryParse<EnumProjectLevel>(projectLevel?.ToString(), out var level),
                a => a.ProjectLevel == level)
            .WhereIF(accountId.HasValue, a => a.CreateUserId == accountId || a.ResponsibleUserId == accountId)
            .OrderBy(a=>a.CreateTime, OrderByType.Desc)
            .ToListAsync();
        if (!res.Any()) return new List<ProjectDisplayResponse>();
        
        var projectIds = res.Select(a=>a.Id).ToList();
        var progressList = await DbContext.Instance.Queryable<TbProjectProgress>()
            .Where(a => projectIds.Contains(a.ProjectId))
            .GroupBy(a => a.ProjectId)
            .Select(a => new {a.ProjectId,a.CreateUserName, CreateTime = SqlFunc.AggregateMax(a.CreateTime)})
            .ToListAsync();

        var list = res.Adapt<List<ProjectDisplayResponse>>();
        foreach (var response in list)
        {
            var progress = progressList.FirstOrDefault(a => a.ProjectId == response.Id);
            response.NewestProgressUserName = progress?.CreateUserName ?? "";
            response.NewestProgressTime = progress?.CreateTime;
        }

        return list;
    }
    
    /// <summary>
    /// 获取项目分页
    /// </summary>
    /// <param name="projectLevel">项目等级</param>
    /// <param name="accountId">发起人或负责人Id</param>
    /// <returns></returns>
    public async Task<Page<ProjectDisplayResponse>> GetPage([FromForm]int pageIndex, [FromForm]int pageSize,[FromForm] int? projectLevel,[FromForm]int? accountId)
    {
        var res = await DbContext.Instance.Queryable<TbProject>()
            .WhereIF(Enum.TryParse<EnumProjectLevel>(projectLevel?.ToString(), out var level),
                a => a.ProjectLevel == level)
            .WhereIF(accountId.HasValue, a => a.CreateUserId == accountId || a.ResponsibleUserId == accountId)
            .OrderBy(a=>a.CreateTime, OrderByType.Desc)
            .ToPageAsync(pageIndex,pageSize);
        if (!res.Items.Any()) return new Page<ProjectDisplayResponse>();
        
        var projectIds = res.Items.Select(a=>a.Id).ToList();
        var progressList = await DbContext.Instance.Queryable<TbProjectProgress>()
            .Where(a => projectIds.Contains(a.ProjectId))
            .GroupBy(a => a.ProjectId)
            .Select(a => new {a.ProjectId,a.CreateUserName, CreateTime = SqlFunc.AggregateMax(a.CreateTime)})
            .ToListAsync();

        var list = res.Adapt<Page<ProjectDisplayResponse>>();
        foreach (var response in list.Items)
        {
            var progress = progressList.FirstOrDefault(a => a.ProjectId == response.Id);
            response.NewestProgressUserName = progress?.CreateUserName ?? "";
            response.NewestProgressTime = progress?.CreateTime;
        }

        return list;
    }

    /// <summary>
    /// 查询项目
    /// </summary>
    /// <param name="keyword">关键词</param>
    /// <returns></returns>
    public async Task<List<TbProject>> GetListByKeyword([FromForm] string keyword)
    {
        var res = await DbContext.Instance.Queryable<TbProject>()
            .Where(a=>a.ProjectName.Contains(keyword))
            .ToListAsync();
        return res;
    } 

    /// <summary>
    /// 获取不同等级的项目数
    /// </summary>
    /// <param name="accountId">创建人/负责人id，不填不限制</param>
    /// <returns></returns>
    public async Task<Dictionary<EnumProjectLevel,int>> GetProjectCount([FromForm] int? accountId)
    {
        var res = await DbContext.Instance.Queryable<TbProject>()
            .WhereIF(accountId.HasValue, a => a.CreateUserId == accountId || a.ResponsibleUserId == accountId)
            .GroupBy(a => a.ProjectLevel)
            .Select(a => new {a.ProjectLevel, Count = SqlFunc.AggregateCount(a.Id)})
            .ToListAsync();
        return res.ToDictionary(a => a.ProjectLevel, a => a.Count);
    }
    
    /// <summary>
    /// 提级申请
    /// </summary>
    /// <param name="projectId">项目id</param>
    /// <param name="reason">提级理由</param>
    /// <param name="toLevel">目标等级</param>
    public async Task ApplyUpgrade([FromForm] int projectId, [FromForm] string reason,[FromForm] int toLevel)
    {
        var users = await DbContext.Instance.Queryable<TbAccount>()
            .Where(a=>a.IsSuperAdmin == true)
            .ToListAsync();
        var upgradeItems = new List<TbProjectUpgrade>();

        foreach (var user in users)
        {
            var upgradeItem = new TbProjectUpgrade
            {
                ProjectId = projectId,
                OperatorId = user.Id,
                OperatorName = user.RealName,
                UpgradeLevel = (EnumProjectLevel)toLevel,
                Reason = reason,
                IsInitiator = user.Id == App.User.GetUserId(),
                IsAgree = user.Id == App.User.GetUserId(),
            };
            upgradeItems.Add(upgradeItem);
            
            if(user.Id != App.User.GetUserId())
            {
                // 发送通知
                await _msgHub.SendProjectUpgradeNotify(user.Id, projectId, $"有新的项目提级申请，需要您处理。");
            }
        }
        await DbContext.Instance.Insertable(upgradeItems).ExecuteCommandAsync();
    }

    /// <summary>
    /// 处理提级申请
    /// </summary>
    /// <param name="projectId">项目id</param>
    /// <param name="reason">理由</param>
    /// <param name="toLevel">目标等级</param>
    /// <param name="isAgree">是否同意</param>
    public async Task ProcessUpgradeApply([FromForm] int projectId, [FromForm] string reason, [FromForm] bool isAgree)
    {
        var upgrade = await DbContext.Instance.Queryable<TbProjectUpgrade>()
            .Where(a => a.ProjectId == projectId && a.OperatorId == App.User.GetUserId())
            .OrderByDescending(a=>a.CreateTime)
            .FirstAsync();
        if (upgrade == null)
            throw new Exception("提级申请不存在");
        upgrade.Reason = reason;
        upgrade.IsAgree = isAgree;
        upgrade.UpdateUserId = App.User.GetUserId();
        upgrade.UpdateUserName = App.User.GetRealName();
        await DbContext.Instance.Updateable<TbProjectUpgrade>(upgrade).ExecuteCommandAsync();

        if (isAgree)
        {
            // 所有都同意的话，修改项目等级
            // var isExist = await DbContext.Instance.Queryable<TbProjectUpgrade>()
            //     .AnyAsync(a => a.ProjectId == projectId && a.UpgradeLevel == toLevel && a.IsAgree != true);
            // if (!isExist)
            await DbContext.Instance.Updateable<TbProject>()
                .SetColumns(a => new()
                {
                    ProjectLevel = upgrade.UpgradeLevel,
                    UpdateUserName = App.User.GetUserName(),
                    UpdateUserId = App.User.GetUserId(),
                    UpdateTime = DateTime.Now
                })
                .Where(a => a.Id == projectId)
                .ExecuteCommandAsync();
        }
    }

    /// <summary>
    /// 获取当前用户参与的提级记录
    /// </summary>
    /// <returns></returns>
    public async Task<List<TbProjectUpgrade>> GetUpgradeRecords()
    {
        var res = await DbContext.Instance.Queryable<TbProjectUpgrade>()
            .Where(a => a.OperatorId == App.User.GetUserId())
            .ToListAsync();
        return res;
    }

    /// <summary>
    /// 获取项目简略时间线
    /// </summary>
    /// <param name="projectId"></param>
    /// <returns></returns>
    public async Task<List<ModelTimeLine>> GetBriefTimeline(int projectId)
    {
        var res = new List<ModelTimeLine>();
        // 入库信息
        var project = await DbContext.Instance.Queryable<TbProject>().Where(a => a.Id == projectId).SingleAsync();
        if(project == null) return res;
        res.Add(new (){Time = project.CreateTime,Content = $"{project.CreateUserName}入库"});
        
        // 跟进信息
        var progressList = await DbContext.Instance.Queryable<TbProjectProgress>()
            .Where(a => a.ProjectId == projectId)
            .OrderBy(a => a.CreateTime)
            .ToListAsync();
            res.AddRange(progressList.Select(a => new ModelTimeLine()
            {
                Time = a.CreateTime,
                Content = $"{a.CreateUserName}{(a.Content =="完结"?"完结":"跟进")}"
            }));
            
        // 提级信息
        var upgradeList = await DbContext.Instance.Queryable<TbProjectUpgrade>()
            .Where(a => a.ProjectId == projectId)
            .OrderBy(a => a.CreateTime)
            .ToListAsync();
        if (upgradeList.Any())
        {
            // upgradeList根据 DateOnly.FromDateTime(a.CreateTime) 和 CreateUserName 去重
            var applyItems = upgradeList.GroupBy(a => (DateOnly.FromDateTime(a.CreateTime), a.CreateUserName))
                .Select(a => a.First())
                .ToList();
            res.AddRange(applyItems.Select(a=>new ModelTimeLine(){Time = a.CreateTime,Content = $"{a.CreateUserName}提级申请"}));
            // 处理提级申请
            res.AddRange(upgradeList.Where(a=>a.CreateUserId != a.UpdateUserId).Select(a=>new ModelTimeLine(){Time = a.CreateTime,Content = $"{a.OperatorName}{(a.IsAgree?"通过":"驳回")}提级"}));
        }

        res = res.OrderBy(a => a.Time).ToList();
        return res;
    }

    /// <summary>
    /// 生成pdf
    /// </summary>
    /// <param name="projectId"></param>
    /// <exception cref="AppFriendlyException"></exception>
    [AllowAnonymous]
    [HttpGet]
    public async Task<FileStreamResult> GeneratorPdf(int projectId)
    {
        QuestPDF.Settings.License = LicenseType.Community;
        
        var project = await Get(projectId);
        if (project == null)
            throw Oops.Bah("未找到项目。");
        
        var progressList = await GetProgress(projectId);
        var stream = new MemoryStream();
        QuestPDF.Fluent.Document.Create(container =>
        {
            container.Page(page =>
            {
                page.Size(PageSizes.A4);
                page.PageColor(Colors.White);
                page.DefaultTextStyle(x => x.FontSize(12));
                page.DefaultTextStyle(x => x.LineHeight(1.5f));
                page.Margin(2, Unit.Centimetre);
        
                page.Header()
                    .Text($"{project.ProjectName}项目进展情况表")
                    .FontSize(20)
                    .Bold()
                    .AlignCenter();
                
                page.Content()
                    .PaddingVertical(1, Unit.Centimetre)
                    .Column(x =>
                    {
                        x.Item().Text($"项目名称：{project.ProjectName}");
                        x.Item().Text($"公司名称：{project.CompanyName}");
                        x.Item().Text($"项目来源：{project.ProjectSource}");
                        x.Item().Text($"项目标签：{string.Join(",",project.ProjectTags)}");
                        x.Item().Text($"项目描述：{project.ProjectDescription}");
                        x.Item().Text($"项目需求：{project.ProjectDemand}");
                        x.Item().Text($"项目等级：{project.ProjectLevel.GetDescription()}");

                        x.Item().Text("");
                        x.Item().Text($"项目进展情况：");
                        foreach (var progress in progressList.OrderBy(a=>a.CreateTime))
                        {
                            var txt =
                                $"      {progress.CreateTime:yyyy年MM月dd日}，跟进用户：{progress.CreateUserName}，跟进信息：{progress.Title}，跟进内容：{progress.Content}";
                            x.Item().Text(txt);
                        }
                        
                    });
        
            });
        })
        .GeneratePdf(stream);
        stream.Position = 0;
        return new FileStreamResult(stream, "application/pdf")
        {
            FileDownloadName = $"{project.ProjectName}.pdf"
        };
    }
}